%{
#include <cstdio>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>

#include "../lib/mydef.h"

#define MAX_HASH_NUM 10

using namespace std;

extern "C" int yylex();
extern "C" int yyparse();
extern "C" FILE *yyin;
int MATCHFLAG=0;
void yyerror(const char *s);

string type;
int hash_cnt=0;
int block_cnt=1;
stack<hash_table> hs;
queue<string> shadow_warning;
hash_table ht[MAX_HASH_NUM];
stringstream ss;
string decl_id;

%}
%defines
%union{
	char *kval;
	char *idval;
	int ival;
	float fval;
	char *sval;
	char *oval;
}

//constant string token
%token <kval> PROGRAM BeGIN END FUNCTION READ WRITE IF ELSIF ENDIF DO WHILE CONTINUE BREAK RETURN INT VOID STRING FLOAT TRUE FALSE
%token <idval> IDENTIFIER
%token <ival> INTLITERAL
%token <fval> FLOATLITERAL
%token <sval> STRINGLITERAL
%token <oval> ASSIGN PLUS MINUS MULTIPLY DIVIDE EQUAL NEQUAL LT GT LQ RQ SEMI COMMA LET GET 
%error-verbose

%%
/* Program */
program:        
	program_start id BeGIN pgm_body END	 
	;

program_start:
	PROGRAM	{
	ht[hash_cnt].hash[M]->name="GLOBAL";
	hs.push(ht[hash_cnt++]);
	}
	;

id:
	IDENTIFIER
	;
pgm_body:
	decl func_declarations
	;
decl:
	 decl string_decl
	|decl var_decl
	|/*empty*/
	;
//decl_tail:
//	decl  decl_tail
//	|/*empty*/
//	;
/* Global String Declaration */
//string_decl_list:
//	string_decl_list_start
//	;

//string_decl_list_start:
//	string_decl_list_start string_decl
//	|string_decl
//	;

string_decl:
	string_start string_id ASSIGN  string_decl_end SEMI;
	;
string_start:
	STRING	{type="STRING";}
	;
string_id:
	id	{decl_id=yylval.idval;}
	;
str:
	STRINGLITERAL
	;
string_decl_end:
	STRINGLITERAL	{
	shadow_warning=stack_search(hs,shadow_warning,decl_id);
	ht[hash_cnt-1].hash_search_insert(decl_id,type,yylval.sval);}
	;
//string_decl_tail:
//	string_decl_tail string_decl
//	|/*empty*/
	;

/* Variable Declaration */
//var_decl_list:
//	var_decl_list_start
//	;
//var_decl_list_start:
//	var_decl_list_start var_decl
//	|var_decl
//	;
var_decl:
	var_type id_list_hash SEMI
	;
var_type:
	FLOAT	{type="FLOAT";}
	| INT	{type="INT";}
	;
any_type:
	var_type
	|VOID	{type="VOID";}
	;
id_list:
	id_decl id_tail
	;
id_tail:
	COMMA id_decl id_tail 
	|/* empty*/
	;
id_decl:
	id
	;
id_list_hash:
	id_decl_hash id_tail_hash
	;
id_tail_hash:
	COMMA id_decl_hash id_tail_hash
	|/*empty*/
	;
id_decl_hash:
	id {
	decl_id=yylval.idval;
	shadow_warning=stack_search(hs,shadow_warning,decl_id);
	ht[hash_cnt-1].hash_search_insert(decl_id,type,"NIL");}
//var_decl_tail:
//	var_decl  var_decl_tail
//	|/*empty*/
//	;

/* Function Paramater List */
param_decl_list:
	param_decl param_decl_tail
	|/*empty*/
	;
param_decl:
	var_type id {
	decl_id=yylval.idval;
	shadow_warning=stack_search(hs,shadow_warning,decl_id);
	ht[hash_cnt-1].hash_search_insert(decl_id,type,"NIL");
	}
	;
param_decl_tail:
	COMMA param_decl param_decl_tail 
	|/* empty*/
	;

/* Function Declarations */
func_declarations:
	func_decl func_decl_tail
	|/*empty*/
	;
func_decl:
	function_start any_type func_id LQ param_decl_list  RQ BeGIN func_body function_end
	;
function_start:
	FUNCTION
	;
function_end:
	END		{hs.pop();}
	;
func_id:
	id	{
	ht[hash_cnt].hash[M]->name=yylval.idval;
	hs.push(ht[hash_cnt++]);}
	;
func_decl_tail:
	func_decl func_decl_tail 
	|/*empty*/
	;
func_body:
	decl stmt_list 
	;

/* Statement List */
stmt_list:
	stmt stmt_tail 
	|/* empty*/
	;
stmt_tail:
	stmt stmt_tail 
	|/* empty*/
	;
stmt:
	base_stmt 
	| if_stmt 
	| do_while_stmt
	;
base_stmt:
	assign_stmt 
	| read_stmt 
	| write_stmt 
	| return_stmt
	;

/* Basic Statements */
assign_stmt:
	assign_expr SEMI
	;
assign_expr:
	id ASSIGN expr
	;
read_stmt:
	READ LQ id_list RQ SEMI
	;
write_stmt:
	WRITE LQ id_list RQ SEMI
	;
return_stmt:
	RETURN expr SEMI
	;

/* Expressions */
expr:
	factor expr_tail 
	;
expr_tail:
	addop factor expr_tail 
	| /*empty*/
	;
factor:
	postfix_expr factor_tail
	;
factor_tail:
	mulop postfix_expr factor_tail 
	|/* empty*/
	;
postfix_expr:
	primary 
	| call_expr
	;
call_expr:
	id LQ expr_list RQ
	;
expr_list:
	expr expr_list_tail
	|/*empty*/
	;
expr_list_tail:
	COMMA expr expr_list_tail 
	|/* empty*/
	;
primary:
	LQ expr RQ
	| id 
	| INTLITERAL 
	| FLOATLITERAL
	;
addop:
	PLUS
	| MINUS
	;
mulop:
	MULTIPLY 
	| DIVIDE
	;

/* Complex Statements and Condition */ 
if_stmt:
	if_start LQ cond RQ  decl stmt_list else_part if_end
	;
if_start:
	IF	{
	ss<<"BLOCK "<<block_cnt++;
	ht[hash_cnt].hash[M]->name=ss.str();
	ss.str("");
	hs.push(ht[hash_cnt++]);}
	;
else_part:
	elif_start LQ cond RQ  decl  stmt_list else_part 
	|/* empty*/
	;
elif_start:
	ELSIF	{
	hs.pop();
	ss<<"BLOCK "<<block_cnt++;
	ht[hash_cnt].hash[M]->name=ss.str();
	ss.str("");
	hs.push(ht[hash_cnt++]);}
	;
if_end:
	ENDIF	{hs.pop()}
cond:
	expr compop expr 
	| TRUE 
	| FALSE
	;
compop :
	LT
	| GT
	| EQUAL
	| NEQUAL 
	| LET 
	| GET
	;

/*while_stmt */
do_while_stmt:
	do_while_start decl  aug_stmt_list while_end
	;
do_while_start:
	DO	{
	ss<<"BLOCK "<<block_cnt++;
	ht[hash_cnt].hash[M]->name=ss.str();
	ss.str("");
	hs.push(ht[hash_cnt++]);
	}
	;
while_end:
	WHILE LQ cond RQ SEMI	{hs.pop();}
	;
/* CONTINUE and BREAK statements. */
aug_stmt_list :
	aug_stmt aug_stmt_list 
	| /*empty*/
	;
aug_stmt:
	base_stmt 
	| aug_if_stmt 
	| do_while_stmt 
	| CONTINUE SEMI 
	| BREAK SEMI
	;

/* Augmented IF statements */ 
aug_if_stmt:
	if_start LQ cond RQ  decl aug_stmt_list aug_else_part if_end
	;
aug_else_part:
	elif_start LQ cond RQ  decl  aug_stmt_list aug_else_part 
	|/* empty*/
	;

%%

main(int argc,char **argv)
{	
	symbol* sym;
	++argv, --argc;
	if (argc > 0)
	{
		yyin=fopen(argv[0],"r");
	//	cout<<"file: "<<argv[0]<<" is read successfully"<<endl;
	}
	
	do{
	//	yydebug=1;
		yyparse();
	//	fprintf(stderr,"/n");

	}while (!feof(yyin));
	if (MATCHFLAG==1)
	{
		ofstream fout("result.txt", ios::app); 
		fout<<argv[0]<<"	accept"<<endl;
	}
	while(!shadow_warning.empty())
	{
		cout<<"SHADOW WARNING "<<shadow_warning.front()<<endl;
		shadow_warning.pop();
	}
	for(int i=0;i<hash_cnt;i++)
	{
		cout<<endl<<"Symbol table "<<ht[i].hash[M]->name<<endl;
		/*for(int j=0;j<M;j++)
		{
			if(ht[i].hash[j]->status=="FIL")
				ht[i].hash[j]->print();
		}*/
		if (ht[i].first!=0)
		{
			sym=ht[i].first;
			sym->print();
			while (sym->next!=0)
			{
				sym=sym->next;
				sym->print();
			}
		}
		
	}
}

void yyerror(const char *s)
{
	cout<<"Not accepted"<<endl;
}

	
